package org.redmagic.utils;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public final class Documents {

    public static Document emptyDocument() {
        return emptyDocument(false);
    }
    
    public static Document emptyDocument(boolean namespaceAware) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(namespaceAware);
            DocumentBuilder docBuilder = factory.newDocumentBuilder();
            return docBuilder.newDocument();
        } catch (ParserConfigurationException pce) {
            throw new RuntimeException(pce.getMessage(), pce);
        }
    }
    
    public static Document parseContents(String fileContents) {
        return parseContents(fileContents, false);
    }

    public static Document parseContents(String fileContents, boolean namespaceAware) {
        return fromInputStream(new ByteArrayInputStream(fileContents.getBytes()), namespaceAware);
    }

    public static Document fromInputStream(InputStream inputStream) {
        return fromInputStream(inputStream, false);
    }

    public static Document fromInputStream(InputStream inputStream, boolean namespaceAware) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(namespaceAware);
            DocumentBuilder docBuilder = factory.newDocumentBuilder();
            Document document = docBuilder.parse(inputStream);
            return document;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    
    @Nonnull
    public static Iterable<Element> getChildElements(Element parentNode) {
        return asIterable(parentNode.getChildNodes());
    }

    @Nonnull
    public static Iterable<? extends Node> filterChildNodes(Element parentNode, short nodeType) {
        return asIterable(parentNode.getChildNodes(), nodeType);
    }
    
    @Nonnull
    @SuppressWarnings("unchecked")
    public static Iterable<Element> asIterable(NodeList nodeList) {
        return (Iterable<Element>) asIterable(nodeList, Node.ELEMENT_NODE);
    }

    @Nonnull
    public static Iterable<? extends Node> asIterable(NodeList nodeList, short nodeType) {
        Conditions.notNull(nodeList, "nodeList");
        List<Element> childElements = new ArrayList<Element>();
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node child = nodeList.item(i);
            
            if (child.getNodeType() == nodeType) {
                childElements.add((Element) child);
            }
        }
        return childElements;
    }

    @Nonnull
    public static Iterable<Element> getSiblingsByTag(Element node, String tag) {
        return null;
    }

    @Nullable
    public static Element getFirstChildByTag(Element parentNode, String tag) {
        Iterable<Element> elements = getElementsByTag(parentNode, tag);
        Iterator<Element> iter = elements.iterator();
        if (iter.hasNext()) {
            return iter.next();
        } else {
            return null;
        }
    }
    
    @Nonnull
    public static Iterable<Element> getElementsByTag(Element element, String tag) {
        NodeList nodeList = element.getElementsByTagName(tag);
        return asIterable(nodeList);
    }
}